import { Injectable } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { Observable, of,NEVER } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

import { LocalStorage } from '../utils/localstorage';
import { NotifyService } from '../utils/notify.service';
import { Log } from '../utils/log';
import { environment } from "../../environments/environment";
import { Message } from "../utils/message";

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor(
    private http: HttpClient,
    private notify: NotifyService
  ) {
  }

  get(path: string, param: any): Observable<any> {
    return this.getHttpHeader().pipe(
      mergeMap(headers => {
        let url = this.getPathParm(path, param);
        return this.http.get<any>(url, { headers: headers });
      }),
      filterErr(this, res => res)
    );
  }

  post(path: string, param: any, data: any): Observable<any> {
    return this.getHttpHeader().pipe(
      mergeMap(headers => {
        let url = this.getPathParm(path, param);
        return this.http.post<any>(url, data, { headers: headers });
      }),
      filterErr(this, res => res)
    );
  }

  put(path: string, param: any, data: any): Observable<any> {
    return this.getHttpHeader().pipe(
      mergeMap(headers => {
        let url = this.getPathParm(path, param);
        return this.http.put<any>(url, data, { headers: headers });
      }),
      filterErr(this, res => res)
    );
  }

  delete(path: string, param: any): Observable<any> {
    return this.getHttpHeader().pipe(
      mergeMap(headers => {
        let url = this.getPathParm(path, param);
        return this.http.delete<any>(url, { headers: headers });
      }),
      filterErr(this, res => res)
    );
  }

  getNoToken(path: string, param: any): Observable<any> {
    let headers: HttpHeaders = new HttpHeaders({ 'Content-type': 'application/json' });
    let url = this.getPathParm(path, param);
    return this.http.get<any>(url, { headers: headers }).pipe(filterErr(this, res => res));
  }

  postNoToken(path: string, data: any): Observable<any> {
    let headers: HttpHeaders = new HttpHeaders({ 'Content-type': 'application/json' });
    let url = this.getPrePath(path);
    return this.http.post<any>(url, data, { headers: headers }).pipe(filterErr(this, res => res));
  }

  blob(path: string, param: any, data: any): Observable<any> {
    return this.getHttpHeader().pipe(
      mergeMap(headers => {
        let url = this.getPathParm(path, param);
        return this.http.post(url, data, { headers: headers, observe: 'response', responseType: 'blob' });
      }),
      filterErr(this, res => res)
    );
  }

  private getErrMsg(status: number): string {
    let msg: string = "获取数据出现问题，请重试。";
    switch (status) {
      case 401:
        msg = "用户认证未通过，请重新登录";
        break;
      case 403:
        msg = "用户认证未通过，请重新登录";
        break;
      case 404:
        msg = "用户认证未通过，请重新登录";
        break;
    }
    return msg;
  }

  private getHttpHeader(): Observable<HttpHeaders> {
    let token = LocalStorage.getToken();
    if (!token) {
      let errInfo: string = Message.getMsg('001007');
      this.outErrMsg(errInfo);
      return NEVER;
    }
    let headers: HttpHeaders = new HttpHeaders({ 'Content-type': 'application/json', 'Authorization': token });
    return of(headers);
  }

  private getPathParm(url: string, param: {}): string {
    return this.getPrePath(url) + this.getUrlParam(param);
  }

  private getUrlParam(param: {}): string {
    if (!param) return "";
    let ret = "";
    for (let key in param) {
      ret += (ret === "" ? "" : "&") + key + "=" + param[key];
    }
    if (ret) {
      ret = "?" + ret;
    }
    return ret;
  }

  private getPrePath(url: string): string {
    let host = environment.baseUrl;
    if (url.startsWith("/") === false) {
      return host + "/" + url;
    }
    return host + url;
  }

  private outErrMsg(errMsg: string, title: string = '错误'): void {
    this.notify.error(errMsg, title).subscribe();
  }


}

/**
 * 拦截处理错误，且错误不再向下传递
 * @param target
 * @param project
 */
function filterErr(target, project) {
  return function mapOperation(source: Observable<any>): Observable<any> {
    return new Observable(observer => {
      source.subscribe(
        val => observer.next(project(val)),
        err => {
          let msg = target.getErrMsg(err.status);
          target.outErrMsg(msg);
          Log.print(err);
          observer.complete();
        },
        () => observer.complete()
      )
    });
  };
}
